昨天介紹了 Berry AI 的得來速 Timer:在得來速車道上架設攝影機,用電腦視覺追蹤每一台車,告訴店家每台車在每一站停留了多久。聽起來蠻單純的吧?裝相機、追車、算時間,感覺找幾位工程師兜一兜就能上線了。
我們一開始也是這麼想的。
所以今天要做的事很簡單:把過去五年遇過的坑攤開來給大家看,一次一百個。
難以控制的物理世界
先從最物理的部分說起。軟體工程師以為的部署是 git push,我們的部署還有梯子、電鑽跟高空作業。
- 得來速場景在戶外。烈日逆光、暴雨淹水、大雪一片白,各種意想不到的場景全年無休輪番上陣,挑戰系統的極限。
- 下雪之後,點餐板旁邊的雪堆會被偵測成車 (真的跟白車很像啊!),只好趕緊收集雪景資料重新訓練模型。
- 一場超大颶風一次打趴十幾間店,打開監控整片紅燈,嚇死 on-call 工程師。
- 有間店一週停電三到四次,PoE switch 陪著一起輪迴。
- 老鼠會咬線,「鼠害」在維修紀錄裡是一個正式分類。
- 鑽牆鑽到鋼梁,鑽頭陣亡;換個位置,牆裡是發泡填充材,螺絲根本鎖不住,相機只好下移三呎,都快拍不到車道了。
- 相機佈線要經過屋頂,到現場才知道屋頂我們沒有進入權限,隔壁那片天花板還屬於另一家公司,相機的佈置計畫只能打掉重練。
- 安裝規範千奇百怪:相機只能裝在建物牆體,樓板不行、點餐面板不行、對講機也不行;北方的店還禁止纜線橫越屋頂,因為冬天鏟雪會鏟壞。
- 白天場勘一切正常,晚上相機旁邊那盞路燈會把畫面照成全白。
- 停車場不給挖溝埋管,佈線又有長度上限,只好研究能跨約數百公尺的無線橋接,還要派人確認那根燈桿上到底有沒有市電。
那台飄洋過海的主機
每一間店裡都有一台我們的主機。它的旅程從實驗室的機櫃開始,經過產線與物流,最後在門市的角落一待就是好幾年。
- 實驗室裡永遠養著一排候選機型輪流跑分:效能、成本、供貨穩定度三個變數,每一季都要重新解一次。
- 新機型上市前要先通過我們的大烤:一台機器同時模擬幾十路錄影加 AI 推論,連續跑上幾週,功耗、溫度、延遲、掉幀通通在監考範圍。
- 店內主機的功耗預算是 CPU 和 GPU 共用的,CPU 吃滿時 GPU 分不到電,推論延遲直接超標。跟晶片原廠回報,一週沒有回信。
- 相機的感光元件會停產,就得趕在庫存燒完前找到第二供應商,而不同元件拍出來的畫面特性不同,還要重新微調 AI 模型。
- 為了避免軟體智財被偷走,主機出貨前要先做好硬碟加密,可是加密的磁碟開機要有人輸入密碼,門市裡可沒有人能幫你輸入呀!
- 運輸過程的顛簸碰撞把硬碟跟 GPU 都摔壞了… 還要研究怎麼包裝才經得起粗魯的物流。
- 錄影、模型、資料庫、日誌,全部擠在店內一顆小硬碟上,誰多吃一點,別人就得餓著 — 空間分配是一場永恆的談判。
- 同一套系統要在好幾個世代的硬體上跑:新店用新機器,老店還是幾年前的機型,模型和服務得同時伺候所有世代。
別人家的網路
設備裝在門市裡,但網路是客戶的、防火牆是數個外包商管的,我們只是借住。寄人籬下的日子不太好過。
- 我們的主機 (edge server) 寄宿在客戶的防火牆之後,也沒有固定的外網 IP。想 SSH 進去?你先研究一下反向代理。
- 裝機當下一切正常,幾小時後,「智慧」的動態防火牆把我們的 VPN 封了…
- 想處理防火牆問題,得先搞清楚這間店面的網路究竟是客戶的三間外包 IT 廠商中的哪一間管。
- 客戶的防火牆會攔截 TLS 連線,把我們的憑證換成它自簽的,驗證直接失敗。
- 請客戶把我們的服務加進白名單,加了網域、沒加 IP 段。偏偏雲端服務每次解析到不同 IP,症狀是時好時壞 — 比全壞更難查。
- 路徑上藏著一個 MTU 黑洞:ping 得通,資料一大就卡死。自動化平台連不上,工程師的筆電卻連得上。
- 店內網路只放行 TCP 的 DNS,而市面上一大半現成工具預設走 UDP、甚至寫死公用 DNS,進了店統統失靈。
- 店內同時出現兩台 DHCP server,主機和相機被分進不同網段,從此互相找不到對方。
- 客戶網路換 subnet 不會通知我們,都是整批掉線了才知道。
- 門市的主網路斷線時,客戶會主動關掉我們的 port,因為怕我們搶備援頻寬。
- 某間店的上行頻寬實測不到 1 Mbps,所以白天不敢回傳錄影怕影響客戶營業,只能利用晚上短短的窗口把調校要用的影片拉回來,上線時程直接被這條頻寬綁住。
- 極端鄉下的店找不到像樣的 ISP,客戶的 IT 拿著政府寬頻地圖一家一家查,衛星網路被當成過渡方案。
很有個性的相機
一切數據的源頭是相機。而相機是嵌入式裝置,每一支都很有自己的想法。
- 某品牌相機有個 bug,會自己卡在一個寫死的靜態 IP。一間店好幾支同時卡住的時候,它們卡在同一個 IP 上。
- 一支相機能同時服務的連線數量只有個位數,規格書還不見得會寫。超過上限之後也不會明講,只是默默開始掉幀。
- 串流一下回報 404、一下 500,排查半天,結論:這支全新的相機是壞的。
- ONVIF 是網路相機的通用標準,但每一家的支援範圍都不同、實作都很有個性,最後只好逆向工程,整合成一套自己順手的工具。
- 要把畫面上的車換算成真實世界的位置,每支相機的鏡頭參數都得拿到手,而相機有幾千支,每一支都要校正。
- 防水失效的相機鏡頭在內側起霧,店員在外面怎麼擦都沒用。
- 其他廠商其他用途的相機不時會出現在我們的 subnet 當中,我們還要有機制能分辨敵我。
- 計時產品的一切,建立在「這一幀影像對應真實世界的哪一刻」,但是相機的 RTC 時鐘不準,給出的時間戳是錯的…
- 串流伺服器遇到不合規的 H.264 串流會直接 crash,而市面上的相機,什麼樣的怪串流都敢送。
- 基於客戶的隱私標準,錄影必須不錄音,但是相機不見得支援關閉麥克風,只好從串流封包中去過濾音訊。
- 相機韌體可以遠端更新,但運氣不好的話,刷壞就是變磚,得派技師到場才救得回來。
- 一間店 40 支相機,每支五個設定步驟,每個步驟一次 SSH round-trip,不搞定併發的話根本裝不完。
不照劇本走的車道
相機裝好、網路接通,接下來輪到主角進場:車、駕駛與店員。沒有一位照劇本走。
- 拖著拖車的車會被偵測成兩台獨立的車。客戶總部接著追問:那腳踏車和重機怎麼算?
- 客戶很想統計沒賺到的營業額,但一台車繞著店面開了一圈,沒停就走了。這到底算「路過」,還是「排隊排到一半放棄」?
- 停車位緊貼著車道,客人經過排隊車道去停車,再走進店裡吃飯,這樣也算放棄排隊嗎?
- 停車場裡只要停著一台比較高的車,後面的隊伍就被擋住。
- 店員會把做好的餐直接送給排隊隊伍裡第二、三台車,讓車直接離開,系統又又又以為這是排隊後放棄。
- 尖峰時店員會拿平板走出來沿車道點餐,訂單時間從此與點餐機脫鉤,只能再開發人車互動的演算法來捕捉點餐行為。
- 人車互動的演算法需要偵測人 (店員),但是不可以偵測到坐在車子裡面的人 (駕駛)…
- 店家用三角錐封掉原本的車道、改走新動線,也沒通知我們。
- 車會倒車重新點餐、會中途換車道、會從點餐機旁邊繞過去。狀態機的特例清單列了十幾種,其中幾種在所有實拍影片裡都找不到範例,只能自己造測試資料。
- 裝修是視角殺手:施工碰歪相機、拆下重裝裝反、新的雨棚整個擋住視線。每次裝修,都是一次驚喜大禮包。
- 店員很熱心地幫我們清潔鏡頭,把相機撞歪,真是謝了…
- 樹會長高長大,長到擋住偵測區。給客戶的改善選項裡有一項是「定期修剪樹木」,附註:不持續修剪會復發。
模型的錯,還是世界的錯?
影像進了主機,換 ML team 上場。這一區的坑有個共通點:看起來都像模型的錯,查下去常常是世界的錯。
- 要追蹤一台車從 A 相機開進 B 相機,得先把兩支相機校正到同一套世界座標。而有時候兩支相機的視野完全沒有重疊,要隔空腦補做校正。
- 想借用 Google Map 衛星圖做校正,但衛星圖太過時,跟現場完全對不起來。
- 排隊的車一台貼著一台,車頭咬著車尾,畫面上的偵測框黏成一團,搞不清楚到底有幾台車啦!
- 夜間照明是另一個世界:某間店的燈光讓偵測整個翻車,一度認真評估過「夜間限定版」AI 模型。
- 美國滿街都是福特皮卡:兩台一模一樣的車一前一後排隊,靠外觀根本分不出誰是誰。
- 約兩成的店面存在樹叢、圍欄等遮擋盲區:車開進去就完全看不見,還得在另一頭認出「這是剛才那台」,並把它看不見的那段時間也算對。
- 影像串流偶爾會掉幀,車在畫面上就像瞬間移動,追蹤演算法還要能接得上。
- 雙點餐機的車道,單從畫面上看不出店家今天使用哪個點餐機,只能靠各種旁敲側擊來推算出合理的數據。
- 有些判斷當下做不了:這台車是在點餐,還是只是塞車暫停?要再多看幾秒才有答案,狀態機得學會回頭修正自己剛剛的決定。
- 每間店上線前要逐店調校,調到一半相機位置被現場人員自行移動,整批錄好的影片作廢重錄。
- 同一支相機底下,短時間的場景多樣性非常有限,想收集多樣的資料讓模型見世面,還要再開發「最大化資料收集多樣性」的演算法。
- 模型的養分是標註資料。要讓標註員高效地看影片、畫框、覆核,只好開發內部專用的標註平台。
- 人工標註的「標準答案」也可能會錯,debug 演算法之前要先 debug 標註。
- 人工標註完整的車輛軌跡要好幾個小時,撐不起大規模部署,只好再開發軌跡標註演算法 — 然後馬上遇到下一題:拿演算法驗演算法,誰當裁判?
- 最刁鑽難解的極端案例幾年也遇不到幾次,等真實資料是等不到的,乾脆用生成式模型把天馬行空的狀況合成進實拍影像,自己製造考題。
- 系統上線之後,就沒有人負責標註與核對正確答案了。想知道演算法還健不健康,還要再開發異常偵測的演算法。
- 平均準確率再漂亮,也掩蓋不了一個被客戶看見的反例:一天五百台車,只要多一台在畫面上卡了 30 秒,店長就會非常在意。
- 相機被撞歪會嚴重影響準確率,而畫面位移偵測的演算法難到我們花了五年才徹底攻破。
一份報表,各自表述
車追完了,接下來要把數字變成客戶手上的看板和報表。聽起來是最平凡的一段,難題卻一點都不少。
- 店經理要的是「斷網也照常運作」,總部要的是「一個畫面看遍一千間店」,這兩個需求把系統劈成店內、雲端兩套。同一份數據,得同時活在兩個世界。
- 不同客戶的「平均等待時間」有好幾種算法:每台車先加總再平均,或每一段各自平均再相加,數學上都對,數字就是不一樣。
- 同一個數據要在店內看板、雲端報表、對外 API 三個地方各算一次,讓三個地方永遠吐出同一個數字,比想像中難得多。
- 所有的數據每天全部重算一遍,遲早吃不消;改成只算新增的部分,就得面對跨日的車、遲到的資料、事後補傳的訂單 — 增量計算的每一個邊界都是坑。
- 車輛軌跡要跟 POS 訂單配對,而訂單會晚到、會補傳,每個品牌的 POS 系統,行為還都不一樣。
- 有的店營業到凌晨三點,凌晨兩點那台車算「今天」還是「昨天」?
- 要搞清楚店家時區,先過「美國地址」這一關:縮寫、錯字、郵遞區號對不上,一家地理編碼服務不夠用,得串好幾層備援互相補位。
- 門市易主是常態,舊老闆時期的歷史數據,要跟著店面走,還是跟著經營者走?連客戶自己都沒有標準答案。
- 總部要看全品牌、加盟主只能看自己的店、店經理只能看自己那一間。同一張報表、一百種視野,資料權限得切到「列」這個粒度才夠用。
- 跨店排行榜要公平:每間店的車道長短、動線都不同,得先設計一套標準化指標,不然短車道的店永遠是冠軍。
- 日報要在店家開門前送達,而每間店有自己的時區、自己的開門時間,偏偏有時店家連自己的營業時間都搞不清楚。
- 店內看板要播即時影像,從 RTMP、HLS 一路遷到 WebRTC,每一代協定都有自己的延遲、相容性跟防火牆問題。
千里之外的一千台機器
機器散落在全美各地的門市裡,而工程師全都在台北。接下來是維運的日常。
- 監控不能只問「還開機嗎」,還要問「資料新鮮嗎」:從偵測、追蹤、計算到看板顯示,每一層都有自己的延遲預算,任何一層塞住,現場店員看到的就會是過時的數據。
- 每間店幾支到幾十支相機、每支都有自己的監控指標,乘上千間店,時序資料庫先喊救命 — 監控系統本身也需要容量規劃與壓力測試。
- 事故發生時,門市往往已經打烊斷網,log 撈不回來,除錯得跟店家的作息賽跑。
- 店家斷網兩三天是常有的事。恢復連線後,累積的資料要回補、指標要重算,還不能把當下的即時數據擠掉。
- 近千台機器要滾動安全性更新,得切成好幾批、避開營業時間,而門市橫跨美國四個時區 — 一輪更新排下來就是一個月。
- 全面升級進行到一半,總有幾間店正好斷網。等它們回線,得自己追上「現在正確的版本」— 要是這段期間新版本又被緊急撤回,事情就更熱鬧了。
- 回歸測試沒有「一份標準答案」:線上並存十來個演算法版本,每個版本算出來的數字都不一樣,期望值要一版一版分開管理。
- 壓力測試的劇本要自己寫:模擬兩倍店數的資料量、尖峰時段的報表排程,先在測試環境把系統壓垮一次,才敢簽下一批店。
規模、客戶,與深呼吸
最後這一組,技術難度不一定最高,但保證最需要深呼吸。
- 客戶評估系統的第一個問題永遠很直接:你們的演算法有多準?有保證嗎? (壓力好大…)
- 數據準確度 95% 的店客訴最多,80% 的店零客訴。客戶想把準確率寫進合約,但「體感準確度」至今沒有公式。
- 客戶會拿著碼表站在車道旁邊,跟系統輸出的數字對答案。每次對不上,查到最後都是碼表按錯,演算法還沒輸過。
- 客訴常常只有一句話:「數字怪怪的」。是哪台車、哪個時段、哪個指標?得先當偵探,才輪得到當工程師。
- 客戶覺得某天的數據怪怪的,隔了一個月才想到要回報 — 但店內硬碟空間有限,影像和日誌不可能無限期保留,等問題送到工程師手上,能查證的資料早就被新資料覆寫掉了。
- 跟品牌總部談好要串接 POS 資料,法務說授權不能集中處理,請跟每一位加盟主分別簽 — 而這個品牌,有幾百位加盟主。
- 每月要新裝 100 到 150 間店,而店在美國、工程團隊在台灣,隔著一個太平洋和一整圈的時差,光是協調就是一大工程。
- 上面九十九條,都不只是一間店的問題,而是上千間店…
小結
以上一百條,沒有一條是編的 (真希望有幾條是編的)。好消息是,其中大多數我們都已經解決,或至少馴服到可控的程度了。明天開始,我們會先從整個系統的架構與團隊分工談起,再由各個 team 輪番上場,說說這些坑是怎麼填起來的。回頭看開頭那句「找幾位工程師兜一兜就能上線」,其實也沒說錯 — 我們就是那幾位工程師,只是這一兜,就兜了五年。
本系列由 Berry AI 工程團隊出品。更多工程實戰紀錄都在 Berry AI 技術部落格。